package fr.lelouet.server.perf.snapshot.receivers;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import fr.lelouet.server.perf.snapshot.ActivityReport;
import fr.lelouet.server.perf.snapshot.HVSnapshot;
import fr.lelouet.server.perf.snapshot.SharedMainConfig;
import fr.lelouet.server.perf.snapshot.SnapshotReceiver;

/**
 * Write incomming data to a directory. All data are supposed to be related to
 * one hypervisor, and are either the data of vms (name starting with "pid:" )
 * or of the hypervisor(name starting with "hv:" )
 * <p>
 * Implements the observer pattern and creates files in a directory according to
 * incomming data
 * </p>
 */
public class DirectoryWriter implements SnapshotReceiver {

	/** a snapshot file should end with that suffix */
	public final static String FILE_EXTENSION = ".hvs";

	/** a directory containing snapshots should start with that prefix */
	public final static String DIR_PREFIX = "snapshots_";

	private static final Logger logger = LoggerFactory
			.getLogger(DirectoryWriter.class);

	private static final long serialVersionUID = 1L;

	/**
	 * the key to specify whether this should write snapshots on any
	 * modifications (true) or only on hypervisor modification (false)
	 */
	public static final String HANDLEVM_KEY = "snapshotDirectoryWriter.writeOnVMModification";

	/** the key for the directory to write snapshots director in */
	public static final String OUTPUTDIR_KEY = "snapshotDirectoryWriter.dirOut";

	// ///////////////////
	// directory management
	// ///////////////////

	protected final String outDirDefaultName = DIR_PREFIX
			+ new SimpleDateFormat("yyyy-MM-dd, HH mm ss").format(new Date());

	/** the name of the directory to write snapshots into */
	protected String outDirName = SharedMainConfig.snapshotsDir;

	/** has the directory already been checked ? */
	boolean dirCreationTryed = false;

	/** the directory to put the snapshots into */
	protected File outDir = null;

	/**
	 * set the dir to write the snapshot dir into. this method is configured via
	 * the OUTPUTDIR_KEY string in the properties
	 */
	public void setDirName(String newPath) {
		outDirName = newPath;
		dirCreationTryed = false;
		outDir = null;
	}

	/**
	 * creates the directory to write snapshots once, and return it
	 * 
	 * @return the directory to put the data into, or null if not able to
	 */
	protected File getOutDir() {
		if (outDir == null) {
			if (dirCreationTryed || outDirName == null) {
				return null;
			} else {
				findDirectory();
			}
		}
		return outDir;
	}

	/**
	 * create the directory named by {@link #outDirName} if it doesn't exist,
	 * remove any file in it if {@link #shouldDeletePresentFiles}. If a file of
	 * name with such a name exist, do nothing.
	 */
	public void findDirectory() {
		dirCreationTryed = true;
		outDir = new File(outDirName + "/" + outDirDefaultName);
		if (!outDir.exists()) {
			boolean success = outDir.mkdirs();
			logger.info("creating logs directory : " + outDir + " , success : "
					+ success);
			return;
		}
	}

	/**
	 * should this write a snapshot every time a VM is modified ? if false,
	 * shnapshots will be written only when an hypervisor modification will
	 * happen
	 */
	protected boolean handleVMs = false;

	/**
	 * @param accept
	 *            should this write a snapshot every time a VM is modified ? If
	 *            false, then only {@link HVSnapshot} will imply the writing of
	 *            the snapshot
	 */
	public void setHandleVMs(boolean accept) {
		handleVMs = accept;
	}

	@Override
	public void configure(Properties prop) {
		setHandleVMs(Boolean.parseBoolean(prop.getProperty(HANDLEVM_KEY,
				"false")));
		setDirName(prop.getProperty(OUTPUTDIR_KEY, "./"));
		getOutDir();
	}

	protected Object writerLock = new Object();

	protected void writeSnapshot(HVSnapshot toPush) {
		File outDir = getOutDir();
		if (outDir != null) {
			try {
				File target = new File(outDir, "snapshot_" + nextSnapshotNumber
						+ FILE_EXTENSION);
				OutputStream stream = new FileOutputStream(target);
				BufferedOutputStream buffer = new BufferedOutputStream(stream);
				ObjectOutputStream output = new ObjectOutputStream(buffer);
				output.writeObject(toPush);
				output.close();
				nextSnapshotNumber++;
			} catch (IOException e) {
				logger.debug("while writting snapshot ", e);
			}
		}
	}

	/** the number of the next snapshot */
	int nextSnapshotNumber = 1;

	private HVSnapshot internalSnapshot = new HVSnapshot();

	@Override
	public void hypervisorModification(HVSnapshot snap, String name) {
		internalSnapshot = new HVSnapshot(snap);
		writeSnapshot(snap);
	}

	@Override
	public void resourceModification(ActivityReport snapshot, String name) {
		if (snapshot.getActivityType().equals(HVSnapshot.HV_ACTIVITY_TYPE)) {
			internalSnapshot.update(snapshot);
			writeSnapshot(internalSnapshot);
		} else if (snapshot.getActivityType().equals(
				ActivityReport.VM_ACTIVITY_TYPE)) {
			internalSnapshot.updateVM(snapshot, name);
			if (handleVMs) {
				writeSnapshot(internalSnapshot);
			}
		}
	}

}
